package logic

import (
	"common/biz"
	"common/logs"
	"common/utils"
	"context"
	"core/repo"
	"core/service"
	"encoding/json"
	"fmt"
	"framework/msError"
	"framework/remote"
	"game_flee/component"
	"game_flee/component/gameFlee"
	"game_flee/models/request"
	"hall/models/response"
	"strconv"
	"sync"
	"time"
)

const (
	UserWebTime = 5 * 60 // 用户访问时间心跳
	waitTime    = 45     //等待倒计时
	attackTime  = 10     //攻击倒计时
	endTime     = 5      //结算倒计时
)

type BigFleeKillManager struct {
	sync.RWMutex
	GameID      uint
	Rounds      int64
	RoomList    []*component.FleeKillRoom
	peopleNum   int64
	users       map[uint]*response.GameFleeData //用户
	usersTimer  map[uint]*time.Timer
	gameStarted bool                //开启，关闭
	status      gameFlee.FleeStatus //是否可加入
	fleeTimer   *time.Timer
	gameService *service.GameService
	gameRule    request.GameFleeRule
	session     *remote.Session
	kills       []uint
	isOpen      int
}

func (m *BigFleeKillManager) sendDataUsers(users []string, data any, session *remote.Session) {
	m.ServerMessagePush(users, data, session)
}

// ServerMessagePush 封装socket推送
func (m *BigFleeKillManager) ServerMessagePush(users []string, data any, session *remote.Session) {
	session.Push(users, data, "ServerMessagePush")
}

func (m *BigFleeKillManager) sendMessagePush(data any) {
	for _, u := range m.users {
		m.sendDataUsers([]string{fmt.Sprintf("%d", u.UserInfo.UID)}, data, u.Session)
	}
}

func (m *BigFleeKillManager) otherUsers(uid uint) []string {
	var uids []string
	for _, v := range m.users {
		if v.UserInfo.UID == uid {
			continue
		}
		uids = append(uids, fmt.Sprintf("%d", v.UserInfo.UID))
	}
	return uids
}

func (m *BigFleeKillManager) run() {
	m.gameRule = m.getGameRule()
	go m.timerFleeStart()
}

// getGameRule 获取配置
func (m *BigFleeKillManager) getGameRule() request.GameFleeRule {
	ctx := context.TODO()
	data := m.gameService.GetConf(ctx, m.GameID)
	var gameRule request.GameFleeRule
	for _, v := range *data {
		if v.Name == "kill_room_num_min" {
			val, _ := strconv.Atoi(v.Value)
			gameRule.KillRoomNumMin = val
		} else if v.Name == "kill_room_num_max" {
			val, _ := strconv.Atoi(v.Value)
			gameRule.KillRoomNumMax = val
		} else if v.Name == "kill_many_rate" {
			val, _ := strconv.Atoi(v.Value)
			gameRule.KillManyRate = int64(val)
		} else if v.Name == "check_user_count" {
			val, _ := strconv.Atoi(v.Value)
			gameRule.CheckUserCount = int64(val)
		} else if v.Name == "check_user_count" {
			val, _ := strconv.Atoi(v.Value)
			gameRule.CheckUserCount = int64(val)
		} else if v.Name == "start_time" {
			val, _ := strconv.Atoi(v.Value)
			gameRule.StartHour = val
		} else if v.Name == "end_time" {
			val, _ := strconv.Atoi(v.Value)
			gameRule.EndHour = val
		} else if v.Name == "open_robot" {
			val, _ := strconv.Atoi(v.Value)
			if val == 1 {
				gameRule.OpenRobot = true
			}
		}
	}
	return gameRule
}

// timerFleeStart 定时开启游戏
func (m *BigFleeKillManager) timerFleeStart() {
	now := time.Now()
	startDate := time.Date(now.Year(), now.Month(), now.Day(), m.gameRule.StartHour, 0, 0, 0, time.Local)
	endDate := time.Date(now.Year(), now.Month(), now.Day(), m.gameRule.EndHour, 0, 0, 0, time.Local)
	//当开始时间大于结束时间 为隔天结束
	if m.gameRule.StartHour > m.gameRule.EndHour {
		//当现在的时间大于0点 并且小于结束时间  为昨天的轮回
		if now.Hour() > 0 && now.Hour() < m.gameRule.EndHour {
			startDate = startDate.AddDate(0, 0, -1)
		} else {
			endDate = endDate.AddDate(0, 0, 1)
		}
	}
	var times int64
	// 现在的时间<=开始时间 && 现在的时间<=结束时间 = （未开始）开始倒计时
	if now.Unix() <= startDate.Unix() && now.Unix() <= endDate.Unix() {
		times = startDate.Unix() - now.Unix()
		m.Rounds = 0
	} else { //游戏中
		diff := now.Sub(startDate)
		hours := diff / time.Hour % 60
		minutes := diff / time.Minute % 60
		//TODO 1分钟游戏
		second := diff / time.Second % 60
		times = 60 - int64(second)
		m.Rounds = int64(minutes + hours*60)
	}
	timer := m.fleeTimer
	if timer != nil {
		timer.Stop()
	}
	timer = time.AfterFunc(time.Duration(times)*time.Second, func() {
		timer.Stop()
		m.gameStarted = true
		//初始化数据
		go m.initData(time.Now())
		//启动定点执行
		go m.roomTimerHandler()
		//定时关闭
		go m.timerFleeStop()
	})
}

// timerFleeStop 定时关闭游戏
func (m *BigFleeKillManager) timerFleeStop() {
	now := time.Now()
	startDate := time.Date(now.Year(), now.Month(), now.Day(), m.gameRule.StartHour, 0, 0, 0, time.Local)
	endDate := time.Date(now.Year(), now.Month(), now.Day(), m.gameRule.EndHour, 0, 0, 0, time.Local)
	//当开始时间大于结束时间 为隔天结束
	if m.gameRule.StartHour > m.gameRule.EndHour {
		//当现在的时间大于0点 并且小于结束时间  为昨天的轮回
		if now.Hour() > 0 && now.Hour() < m.gameRule.EndHour {
			startDate = startDate.AddDate(0, 0, -1)
		} else {
			endDate = endDate.AddDate(0, 0, 1)
		}
	}
	var times int64
	times = endDate.Unix() - now.Unix()

	timer := m.fleeTimer
	if timer != nil {
		timer.Stop()
	}
	timer = time.AfterFunc(time.Duration(times)*time.Second, func() {
		logs.Warn("%v关闭大逃杀", time.Now().Format(time.DateTime))
		timer.Stop()
		m.gameStarted = false
		m.status = gameFlee.StatusNone
		m.initRoom(m.GameID)
		//重启开始
		go m.timerFleeStart()
	})
}

// roomTimerHandler 游戏中定时执行业务
func (m *BigFleeKillManager) roomTimerHandler() {
	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()
	for {
		if m.gameStarted == false {
			break
		}
		select {
		case t := <-ticker.C:
			if t.Second() > 0 && t.Second() < waitTime {
				if m.gameRule.OpenRobot {
					go m.checkPeopleJoinRobot(t.Second())
				}
			}
			switch t.Second() {
			//1分钟游戏
			case 0:
				//初始化数据
				go m.initData(t)
				break
			case waitTime:
				go m.sendStatus(gameFlee.StatusEnd, attackTime)
				go m.killRoomHandler()
				break
			case waitTime + attackTime:
				go m.sendStatus(gameFlee.StatusBalance, endTime)
				break
			}
		}
	}
}

// initData 初始化数据
func (m *BigFleeKillManager) initData(t time.Time) {
	m.Lock()
	defer m.Unlock()
	//告诉回合数
	m.Rounds++
	m.kills = make([]uint, 0)
	m.sendMessagePush(gameFlee.GameBureauPushData(m.Rounds))
	go m.sendStatus(gameFlee.StatusBetting, waitTime)
	m.peopleNum = 0
	for _, user := range m.users {
		user.UserInfo.BettingNum = 0
	}
	for _, room := range m.RoomList {
		room.InitRoom()
	}
	//告诉用户房间信息
	m.sendMessagePush(gameFlee.GameRoomPushData(m.RoomList))
	logs.Warn("第%d回合,大逃杀游戏重置%v", m.Rounds, t.Format(time.DateTime))
	m.gameRule = m.getGameRule()
	ctx := context.TODO()
	isOpen := m.gameService.GetConfInfo(ctx, m.GameID, "is_open")
	isOpenInt, _ := strconv.Atoi(isOpen.Value)
	m.isOpen = isOpenInt
	go m.killRoom()
}

// killRoom 杀房间号
func (m *BigFleeKillManager) killRoom() {
	//随机杀死数量
	killRands := utils.Random(1, 100)
	var killNum int64
	if killRands > 1 && killRands <= m.gameRule.KillManyRate {
		killNum = utils.Random(m.gameRule.KillRoomNumMin, m.gameRule.KillRoomNumMax)
	} else {
		killNum = utils.Random(1, 2)
	}
	killNum = 1
	//根据杀死数量随机杀死的房间
	roomId := []uint{1, 2, 3, 4, 5}
	for i := 0; i < int(killNum); i++ {
		rands := utils.Random(0, len(roomId)-1)
		m.kills = append(m.kills, roomId[rands])
		roomId = append(roomId[:rands], roomId[rands+1:]...)
	}
}

// checkPeopleJoinManMachine 填充人机
func (m *BigFleeKillManager) checkPeopleJoinRobot(second int) {
	ctx := context.TODO()
	if m.isOpen != 1 {
		//游戏维护
		return
	}
	robotConf := m.gameService.FleeRobotConf(ctx, second)
	if robotConf == nil {
		return
	}
	if robotConf.JoinMax == 0 {
		return
	}
	//随机人数
	peopleRand := utils.Random(robotConf.JoinMin, robotConf.JoinMax)
	//fmt.Printf("第%d秒检测人数,随机加入人机数%d\n", second, peopleRand)
	if peopleRand <= 0 {
		return
	}
	getUsersDiff := m.gameService.GetFleeUsersDiff(ctx, peopleRand, m.Rounds)
	var session *remote.Session
	for _, v := range m.users {
		session = v.Session
		break
	}
	for _, v := range *getUsersDiff {
		//随机下注数量
		num := utils.Random(robotConf.BettingMin, robotConf.BettingMax) * 10
		roomID := utils.Random(1, len(m.RoomList))
		roomId := uint(roomID)
		room := m.RoomList[roomId-1]
		_, err := m.gameService.FleeBetting(ctx, roomId, 0, room.Name, v.UID, m.Rounds, num, true)
		if err != nil {
			continue
		}
		v.Gold -= num
		v.BettingNum += num
		v.Nickname = utils.UpNickname(v.Nickname)
		room.Betting(&v, num)
		//通知其他用户本玩家加入了房间并下注
		if session != nil {
			m.sendDataUsers(m.otherUsers(v.UID), gameFlee.GameBettingPushData(roomId, 0, v, 0), session)
		}
		m.peopleNum++
	}
}

// killRoomHandler 杀房间
func (m *BigFleeKillManager) killRoomHandler() {
	//修改房间的状态为被杀死 添加记录 发送被杀的房间
	ctx := context.TODO()
	for _, roomID := range m.kills {
		room := m.RoomList[roomID-1]
		_, _ = m.gameService.KillRoom(ctx, m.Rounds, roomID, room.Name)
		room.IsKill = true
		room.BettingNum = 0
	}
	killRoomJson, _ := json.Marshal(m.kills)
	logs.Warn("第%d回合,杀死房间数%d,杀死房间%v", m.Rounds, len(m.kills), string(killRoomJson))
	//m.sendMessagePush(game_flee.GameKillPushData(m.kills))
	go m.endRoom()
}

// endRoom 结算
func (m *BigFleeKillManager) endRoom() {
	ctx := context.TODO()
	users, err := m.gameService.EndGameFlee(ctx, m.Rounds)
	if err != nil {
		logs.Error("%v", err)
		return
	}
	for _, v := range *users {
		uidStr := []string{fmt.Sprint(v.UID)}
		if m.users[v.UID] != nil {
			m.sendDataUsers(uidStr, gameFlee.GameResultPushData(v.IsKill, v.IsDecline, v.ReturnNum, v.Reward), m.users[v.UID].Session)
		}
	}
}

// GetRoom 房间列表
func (m *BigFleeKillManager) GetRoom(session *remote.Session, userData *response.GameFleeInfo) map[string]any {
	ctx := context.TODO()
	res := make(map[string]any)
	res = map[string]any{
		"roomList":   m.RoomList,
		"gameStatus": m.gameStarted,
		"status":     m.status,
		"rounds":     m.Rounds,
		"gold":       userData.Gold,
		"roomID":     0,
	}
	//将用户写入大厅
	m.checkRoomUser(session, *userData)
	user := m.gameService.GetFleeBetting(ctx, userData.UID, m.Rounds)
	if user != nil && user.UID != 0 {
		m.users[userData.UID].UserInfo.BettingNum = user.Num
		res["roomID"] = user.RoomID
		userData.BettingNum = user.Num
	}
	res["user"] = userData
	//res["num"] = m.users[userData.UID].UserInfo.BettingNum
	return res
}

// checkRoomUser 检查房间用户心跳
func (m *BigFleeKillManager) checkRoomUser(session *remote.Session, data response.GameFleeInfo) {
	m.Lock()
	defer m.Unlock()
	_, ok := m.users[data.UID]
	if m.session == nil {
		m.session = session
	}
	if ok {
		//重启用户心跳
		m.usersTimer[data.UID].Reset(UserWebTime * time.Second)
		return
	}
	data.Nickname = utils.UpNickname(data.Nickname)
	m.users[data.UID] = &response.GameFleeData{
		UserInfo: data,
		Session:  session,
	}
	//添加用户心跳
	go m.scheduleUserTimerHandle(data)
}

// FleeOut 退出游戏
func (m *BigFleeKillManager) FleeOut(data *response.GameFleeInfo) {
	m.Lock()
	defer m.Unlock()
	if m.users[data.UID] != nil {
		m.usersTimer[data.UID].Stop()
		delete(m.users, data.UID)
	}
}

// scheduleUserTimerHandle 心跳 定时删除用户组里的用户数据
func (m *BigFleeKillManager) scheduleUserTimerHandle(data response.GameFleeInfo) {
	m.Lock()
	defer m.Unlock()
	if m.usersTimer[data.UID] != nil {
		m.usersTimer[data.UID].Stop()
	}
	m.usersTimer[data.UID] = time.AfterFunc(UserWebTime*time.Second, func() {
		m.usersTimer[data.UID].Stop()
		m.sendDataUsers([]string{fmt.Sprint(data.UID)}, gameFlee.GameOutPushData(), m.users[data.UID].Session)
		delete(m.usersTimer, data.UID)
		delete(m.users, data.UID)
	})
}

func (m *BigFleeKillManager) GetUserAll() map[uint]*response.GameFleeData {
	return m.users
}

func (m *BigFleeKillManager) RoomBetting(session *remote.Session, req *request.RoomFleeBetting, userData *response.GameFleeInfo) (map[string]any, *msError.Error) {
	res := make(map[string]any)
	res = map[string]any{
		"roomList":   m.RoomList,
		"gameStatus": m.gameStarted,
		"status":     m.status,
		"rounds":     m.Rounds,
	}
	if !m.gameStarted {
		logs.Error("[Game]用户%d FleeBetting err:大逃杀未达到开启时间 req=%v", userData.UID, req)
		return nil, biz.GameNotEnabledError
	}
	if m.status != gameFlee.StatusBetting {
		logs.Error("[Game]用户%d FleeBetting err:大逃杀当前时间不可下注 req=%v", userData.UID, req)
		return nil, biz.GameNotJoinError
	}
	//将用户写入大厅
	m.checkRoomUser(session, *userData)
	if req.RoomID < 1 && req.RoomID > 5 {
		logs.Error("[Game]用户%d FleeBetting err:房间ID错误 req=%v", userData.UID, req)
		return nil, biz.GameNotRoom
	}
	roomId := req.RoomID - 1

	room := m.RoomList[roomId]
	if m.RoomList[roomId] == nil {
		logs.Error("[Game]用户%d FleeBetting err:大逃杀未达到开启时间 req=%v", userData.UID, req)
		return nil, biz.GameNotRoom
	}
	if req.Num == 0 || req.Num%10 != 0 {
		logs.Error("[Game]用户%d FleeBetting err:数量为0或不为10的倍数 req=%v", userData.UID, req)
		return nil, biz.GameInputTenMultipleError
	}
	if req.Num > userData.Gold {
		logs.Error("[Game]用户%d FleeBetting err:游戏币余额不足 req=%v", userData.UID, req)
		return nil, biz.GameCurrencyError
	}
	ctx := context.TODO()
	//获取上一把数据
	//if m.Rounds > 1 {
	//	user := m.gameService.GetLastFleeBetting(ctx, m.Rounds-1)
	//	if user != nil && user.UID != 0 && user.UID == userData.UID {
	//		logs.Error("[Game]用户%d FleeBetting err:衰神本回合不可下注 req=%v", userData.UID, req)
	//		return nil, biz.GameDeclineError
	//	}
	//}
	if m.isOpen != 1 {
		//游戏维护
		logs.Error("[Game]用户%d FleeBetting err:游戏维护 req=%v", userData.UID, req)
		return nil, biz.ServerMaintenance
	}
	user := m.gameService.GetFleeBetting(ctx, userData.UID, m.Rounds)
	var myRoomID uint
	var myNum int64
	if user != nil {
		myRoomID = user.RoomID
		myNum = user.Num
	}
	//if user != nil && user.RoomID != req.RoomID {
	//	logs.Error("[Game]用户%d FleeBetting err:已选择房间不能更改房间 req=%v", userData.UID, req)
	//	return nil, biz.SqlError
	//}

	//1.投入游戏币到房间内
	_, err := m.gameService.FleeBetting(ctx, req.RoomID, myRoomID, room.Name, userData.UID, m.Rounds, req.Num, false)
	if err != nil {
		return nil, err
	}
	//2.更新房间内用户数据
	m.users[userData.UID].UserInfo.Gold = userData.Gold - req.Num
	m.users[userData.UID].UserInfo.BettingNum += req.Num
	userData.Nickname = utils.UpNickname(userData.Nickname)
	res["gold"] = m.users[userData.UID].UserInfo.Gold
	res["num"] = m.users[userData.UID].UserInfo.BettingNum
	if user == nil || user.UID == 0 {
		m.peopleNum++
		userData.BettingNum = req.Num
		room.Betting(userData, req.Num)
	} else {
		myRoom := m.RoomList[myRoomID-1]
		myRoom.Deduction(user.UID, myNum)
		userData.BettingNum = myNum + req.Num
		room.Betting(userData, userData.BettingNum)
	}
	//通知其他用户本玩家加入了房间并下注
	m.sendDataUsers(m.otherUsers(userData.UID), gameFlee.GameBettingPushData(req.RoomID, myRoomID, userData, myNum), session)
	return res, nil
}

// FleeChangeRoom 换房间
func (m *BigFleeKillManager) FleeChangeRoom(session *remote.Session, req *request.RoomChangeRoom, userData *response.GameFleeInfo) (any, *msError.Error) {
	if !m.gameStarted {
		logs.Error("[Game]用户%d FleeChangeRoom err:大逃杀未达到开启时间 req=%v", userData.UID, req)
		return nil, biz.GameNotEnabledError
	}
	if m.status != gameFlee.StatusBetting {
		logs.Error("[Game]用户%d FleeChangeRoom err:大逃杀当前时间不可下注 req=%v", userData.UID, req)
		return nil, biz.GameNotJoinError
	}
	//将用户写入大厅
	m.checkRoomUser(session, *userData)
	if req.RoomID < 1 && req.RoomID > 5 {
		logs.Error("[Game]用户%d FleeChangeRoom err:房间ID错误 req=%v", userData.UID, req)
		return nil, biz.GameNotRoom
	}
	roomId := req.RoomID - 1

	room := m.RoomList[roomId]
	if m.RoomList[roomId] == nil {
		logs.Error("[Game]用户%d FleeChangeRoom err:大逃杀未达到开启时间 req=%v", userData.UID, req)
		return nil, biz.GameNotRoom
	}
	ctx := context.TODO()
	user := m.gameService.GetFleeBetting(ctx, userData.UID, m.Rounds)
	if user == nil || user.UID == 0 {
		logs.Error("[Game]用户%d FleeChangeRoom err:未下注不能换房间 req=%v", userData.UID, req)
		return nil, biz.GameNotBettingChangeRoomError
	}
	if req.RoomID == user.RoomID {
		logs.Error("[Game]用户%d FleeChangeRoom err:不能换到自己房间 req=%v", userData.UID, req)
		return nil, biz.SqlError
	}
	is := m.gameService.FleeChangeRoomBool(ctx, user.UID, m.Rounds)
	if is {
		logs.Error("[Game]用户%d FleeChangeRoom err:每隔5s可更换一次房间，请稍后再试 req=%v", userData.UID, req)
		return nil, biz.GameChangeRoomCountError
	}
	//更换房间
	err := m.gameService.FleeChangeRoom(ctx, req.RoomID, user.RoomID, room.Name, userData.UID, m.Rounds)
	if err != nil {
		return nil, err
	}
	myRoom := m.RoomList[user.RoomID-1]
	myRoom.Deduction(user.UID, user.Num)
	userData.BettingNum = user.Num
	room.Betting(userData, user.Num)
	//通知其他用户本玩家加入了房间并下注
	res := map[string]any{
		"roomID":        user.RoomID,
		"changeRoomID":  req.RoomID,
		"user":          &userData,
		"changeRoomNum": user.Num,
	}
	userDataUp := *userData
	userDataUp.Nickname = utils.UpNickname(userDataUp.Nickname)
	m.sendDataUsers(m.otherUsers(userData.UID), gameFlee.GameBettingPushData(req.RoomID, user.RoomID, userDataUp, user.Num), session)
	return res, nil
}

func (m *BigFleeKillManager) FleeLog(ctx context.Context, uid uint, req *request.PageMsg) map[string]any {
	count, log := m.gameService.FleeLog(ctx, uid, req)
	log50 := make([]any, 0)
	for _, v := range m.RoomList {
		log50 = append(log50, map[string]any{
			"id":        v.ID,
			"name":      v.Name,
			"killCount": m.gameService.FleeKillCount(ctx, v.ID, m.Rounds),
		})
	}
	return map[string]any{
		"log50":    log50,
		"logCount": count,
		"data":     log,
	}
}

func (m *BigFleeKillManager) sendStatus(status gameFlee.FleeStatus, times int) {
	m.status = status
	kills := make([]uint, 0)
	if m.status == gameFlee.StatusEnd {
		kills = m.kills
	}
	m.sendMessagePush(gameFlee.GameStatusPushData(m.status, times, kills))
}

func (m *BigFleeKillManager) initRoom(gameID uint) {
	fleeRoom := make([]request.FleeRoom, 0, 8)
	roomNames := map[uint]string{
		1: "竟燃",
		2: "神觅",
		3: "构梦",
		4: "星图",
		5: "素星",
		//6: "月牙岛",
		//7: "珊瑚岛",
		//8: "橘子岛",
	}
	for i := 1; i <= len(roomNames); i++ {
		roomID := uint(i)
		fleeRoomInfo := request.FleeRoom{
			ID:         roomID,
			Name:       roomNames[roomID],
			BettingNum: 0,
		}
		fleeRoom = append(fleeRoom, fleeRoomInfo)
	}
	for k, v := range fleeRoom {
		newRoom := component.NewFleeKillRoom(v, gameID, m, m.gameService)
		m.RoomList[k] = newRoom
	}
}

func NewCBigFleeKillManager(r *repo.Manager) *BigFleeKillManager {
	m := &BigFleeKillManager{
		GameID:      2,
		RoomList:    make([]*component.FleeKillRoom, 5),
		users:       make(map[uint]*response.GameFleeData),
		usersTimer:  make(map[uint]*time.Timer),
		gameStarted: false,
		status:      gameFlee.StatusNone,
		gameService: service.NewGameService(r),
		kills:       make([]uint, 0),
	}
	m.initRoom(m.GameID)
	m.run()
	return m
}
